String.prototype.leftPad = function(len, pad) {

	if (typeof(len) == "undefined") { var len = 0; }
	if (typeof(pad) == "undefined") { var pad = ' '; }

	
	var padded = '';
	if (len + 1 >= this.length) {
		padded = Array(len + 1 - this.length).join(pad) + this;
		//str = str + Array(len + 1 - str.length).join(pad);
	}
	return padded;
};

var Card = Backbone.Model.extend({
	urlRoot: '/card',
});

var Cards = Backbone.Collection.extend({
	url: '/card',
	model: Card
});

var DeckMember = Backbone.Model.extend({
	parse: function(response) {
		response.card = _.clone(conquest.dict.findCard(parseInt(response.cardId)));
		response.fixedQuantity = response.card.type === 'warlord' || _.isNumber(response.card.warlordId);
		return response;
	}
});

var DeckMembers = Backbone.Collection.extend({
	model: DeckMember,
	comparator: function(one, two) {
		var result;
		if (one.get('card').type === 'warlord') {
			result = -1;
		} else if (two.get('card').type === 'warlord') {
			result = 1;
		} else {
			result = 0;
		}

		if (result === 0) {
			if (!_.isUndefined(one.get('card').warlordId) && _.isUndefined(two.get('card').warlordId)) {
				result = -1;
			} else if (!_.isUndefined(two.get('card').warlordId) && _.isUndefined(one.get('card').warlordId)) {
				result = 1;
			} else {
				result = 0;
			};
		}

		if (result === 0) {
			result = one.get('card').factionDisplay.localeCompare(two.get('card').factionDisplay);
		}

		if (result === 0) {
			result = one.get('card').name.localeCompare(two.get('card').name);
		}
		return result;
	},
	computeTotalQuantity: function() {
		var total = 0;
		this.each(function(member) {
			total += member.get('quantity');
		});
		return total;
	},
	computeTotalCost: function() {
		var total = 0;
		this.each(function(member) {
			total += member.get('quantity') * member.get('card').cost;
		});
		return total;
	},
	adjustQuantities: function(csQuantity) {
		this.each(function(member) {
			if (member.get('fixedQuantity') === false) {
				var availableQuantity = Math.min(3, member.get('card').quantity * csQuantity);
				member.set({
					availableQuantity: availableQuantity
				});
				member.set({
					quantity: Math.min(member.get('quantity'), availableQuantity)
				}, {
					batchChange: true
				});
			}
		});
	}
});

var Deck = Backbone.Model.extend({
	urlRoot: '/deck',
	parse: function(response) {
		response.members = new DeckMembers(response.members, {
			parse: true
		});
		response.members.each(function(member) {
			var cardQuantity = member.get('card').quantity;
			var availableQuantity;
			if (member.get('fixedQuantity') === true) {
				availableQuantity = cardQuantity;
			} else {
				availableQuantity = Math.min(3, cardQuantity * response.configCsQuantity);
			}
			member.set({
				availableQuantity: availableQuantity
			});
		});
		response.filteredMembers = new DeckMembers();
		response.warlord = _.clone(conquest.dict.findCard(parseInt(response.warlordId)));

		return response;
	},
	toJSON: function() {
		var json = Deck.__super__.toJSON.apply(this, arguments);
		if (json.members instanceof Backbone.Collection) {
			json.members = json.members.toJSON();
		}
		if (json.warlord instanceof Backbone.Model) {
			json.warlord = json.warlord.toJSON();
		}
		return json;
	},
	sync: function(method, source, options) {
		console.info('sync: ' + method);
		var target = source;
		if (method === 'create' || method === 'update') {
			var sourceJson = source.toJSON();
			delete sourceJson.warlord;
			delete sourceJson.filteredMembers;			
			if (_.isArray(sourceJson.members)) {
				_.each(sourceJson.members, function(member) {
					delete member.card;
					delete member.fixedQuantity;
					delete member.availableQuantity;
				});
			}
			target = new Deck(sourceJson);
		}

		Deck.__super__.sync.apply(this, [method, target, options]);
	},
	validate: function(attributes, options) {		
		var name = $.trim(attributes.name);
		if (name.length == 0) {
			return 'error.deck.name.empty';
		}
		var other = conquest.getDeck(name);
		var id = attributes.id;
		if (other && (_.isUndefined(id) || other.get('id') !== id)) {
			return 'error.deck.name.duplicate';	
		}
	},
	computeTotalQuantity: function() {
		var total = 0;
		if (this.get('members') instanceof Backbone.Collection) {
			total = this.get('members').computeTotalQuantity();
		}
		return total;
	},
	computeTotalCost: function() {
		var total = 0;
		if (this.get('members') instanceof Backbone.Collection) {
			total = this.get('members').computeTotalCost();
		}
		return total;
	},
	adjustQuantities: function() {
		if (this.get('members') instanceof Backbone.Collection) {
			this.get('members').adjustQuantities(this.get('configCsQuantity'));
		}
	}
});

var Decks = Backbone.Collection.extend({
	url: '/deck',
	model: Deck,
	comparator: 'name'
});

var conquest = conquest || {};
conquest.dict = conquest.dict || {};

(function(d) {

	var IDX_CARD_BY_ID = "card#id";
	var IDX_CARD_BY_SET_NO_CARD_NO = "card#setNumber#cardNumber";
	var IDX_SET_BY_ID = "set#id";
	var indexes = {};

	d.initialize = function() {
		indexes[IDX_SET_BY_ID] = _.indexBy(d.sets, function(set) {
			return set.id;
		});
		indexes[IDX_CARD_BY_ID] = _.indexBy(d.cards, function(card) {
			return card.id;
		});
		indexes[IDX_CARD_BY_SET_NO_CARD_NO] = _.indexBy(d.cards, function(card) {
			return d.findSet(card.setId).number + '#' + card.number;
		});

		_.each(d.cards, function(card) {
			var set = d.findSet(card.setId)
			card.setName = set.name;
			card.setTechName = set.techName;
			card.setNumber = set.number;
		});
	};

	d.findSet = function(id) {
		return indexes[IDX_SET_BY_ID][id];
	};

	d.findCard = function(id) {
		return indexes[IDX_CARD_BY_ID][id];
	};

	d.findCardByNumber = function(setNumber, cardNumber) {
		return indexes[IDX_CARD_BY_SET_NO_CARD_NO][setNumber + '#' + cardNumber];
	};

})(conquest.dict);

(function(c) {

	c.static = {};
	// c.cards = new Cards();
	c.decks = new Decks();
	c.factions = ['astra-militarum', 'space-marines', 'tau', 'eldar', 'dark-eldar', 'chaos', 'ork'];

	// c.resetCards = function(_cards) {
	// 	c.cards.reset(_cards);
	// };

	c.resetDecks = function(_decks) {
		c.decks.reset(_decks, {
			parse: true
		});
	};

	// c.getCards = function() {
	// 	return c.cards;
	// };

	c.getDecks = function() {
		return c.decks;
	};

	c.getFactions = function() {
		return c.factions;
	};

	c.getValidDeckFactions = function(faction) {
		var validDeckFactions = [];
		if (faction != 'neutral') {
			var index = c.factions.indexOf(faction);
			validDeckFactions.push(faction);
			validDeckFactions.push(c.factions[(index - 1 + c.factions.length) % c.factions.length]);
			validDeckFactions.push(c.factions[(index + 1) % c.factions.length]);
			validDeckFactions.push('neutral');
		}
		return validDeckFactions;
	};

	c.getValidDeckCards = function(deckWarlordId) {
		deckWarlordId = parseInt(deckWarlordId);

		var deckWarlord = c.dict.findCard(deckWarlordId);
		var deckFaction = deckWarlord.faction;
		var validDeckFactions = c.getValidDeckFactions(deckFaction);
		var validDeckCards = _.filter(c.dict.cards, function(card) {
			// invalid faction
			var faction = card.faction;
			if (validDeckFactions.indexOf(faction) == -1) {
				return false;
			}
			// invalid type
			var type = card.type;
			if (type === 'token' || type === 'planet') {
				return false;
			}
			// invalid warlord
			if (type === 'warlord' /* && card.get('id') !== deckWarlordId*/ ) {
				return false;
			}
			// invalid signature squad
			var warlordId = card.warlordId;
			if (!_.isUndefined(warlordId) && warlordId !== deckWarlordId) {
				return false;
			}
			// loyal to another faction
			if (card.loyal === true && faction !== deckFaction) {
				return false;
			}
			return true;
		});
		return validDeckCards;
	};

	c.getValidDeckMembers = function(deckWarlordId) {
		var validDeckCards = c.getValidDeckCards(deckWarlordId);
		var validDeckMembers = [];
		_.each(validDeckCards, function(card) {
			var availableQuantity = (_.isUndefined(card.quantity) ? 3 : card.quantity);
			var quantity = (card.type === 'warlord' || _.isNumber(card.warlordId) ? availableQuantity : 0);
			var member = {
				cardId: card.id,
				quantity: quantity,
				availableQuantity: availableQuantity
			};
			validDeckMembers.push(member);
		});
		return validDeckMembers;
	};

	c.getSetTree = function() {
		var tree = {nodes: []};
		var cycleNode = undefined;

		_.each(_.sortBy(c.dict.sets, 'sequence'), function(set) {
			var nodes;
			if (set.cycleTechName) {
				if (_.isUndefined(cycleNode) || cycleNode.techName !== set.cycleTechName) {
					cycleNode = {
						name: set.cycleName,
						techName: set.cycleTechName,
						nodes: []
					};
					tree.nodes.push(cycleNode);					
				}
				nodes = cycleNode.nodes;
			} else {
				nodes = tree.nodes;
			}

			nodes.push({
				name: set.name, 
				techName: set.techName
			});
		});
		return tree;
	};

	c.getDeck = function(value) {
		var query;
		if (_.isNumber(value)) {
			query = {id: value};
		} else if (_.isString(value)) {
			query = {name: value};
		} else {
			query = value;
		}
		return c.decks.findWhere(query);
	};

	c.saveDeck = function(deck) {
		var oldDeck = c.decks.findWhere({id: deck.get('id')});
		if (_.isObject(oldDeck)) {
			c.decks.remove(oldDeck);			
		}
		c.decks.add(deck);
	};

	c.deleteDeck = function(deck) {
		var oldDeck = c.decks.findWhere({id: deck.get('id')});
		if (_.isObject(oldDeck)) {
			c.decks.remove(oldDeck);			
		}
	};

	// c.cloneCardAttrs = function(id) {
	// 	return _.clone(c.dict.findCard(parseInt(id)));
	// };
})(conquest);